home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / js / calItemBase.js < prev    next >
Text File  |  2008-01-02  |  26KB  |  802 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Oracle Corporation code.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  *  Oracle Corporation
  19.  * Portions created by the Initial Developer are Copyright (C) 2004
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
  24.  *   Mike Shaver <shaver@off.net>
  25.  *   Joey Minta <jminta@gmail.com>
  26.  *   Matthew Willis <lilmatt@mozilla.com>
  27.  *   Daniel Boelzle <daniel.boelzle@sun.com>
  28.  *
  29.  * Alternatively, the contents of this file may be used under the terms of
  30.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  31.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  32.  * in which case the provisions of the GPL or the LGPL are applicable instead
  33.  * of those above. If you wish to allow use of your version of this file only
  34.  * under the terms of either the GPL or the LGPL, and not to allow others to
  35.  * use your version of this file under the terms of the MPL, indicate your
  36.  * decision by deleting the provisions above and replace them with the notice
  37.  * and other provisions required by the GPL or the LGPL. If you do not delete
  38.  * the provisions above, a recipient may use your version of this file under
  39.  * the terms of any one of the MPL, the GPL or the LGPL.
  40.  *
  41.  * ***** END LICENSE BLOCK ***** */
  42.  
  43. //
  44. // calItemBase.js
  45. //
  46.  
  47. const ICAL = Components.interfaces.calIIcalComponent;
  48.  
  49.  
  50. function calItemBase() {
  51.     ASSERT(false, "Inheriting objects call initItemBase!");
  52. }
  53.  
  54. calItemBase.prototype = {
  55.     mPropertyParams: null,
  56.     mIsProxy: false,
  57.  
  58.     QueryInterface: function (aIID) {
  59.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  60.             !aIID.equals(Components.interfaces.calIItemBase))
  61.         {
  62.             throw Components.results.NS_ERROR_NO_INTERFACE;
  63.         }
  64.  
  65.         return this;
  66.     },
  67.  
  68.     mHashId: null,
  69.     get hashId() {
  70.         if (this.mHashId === null) {
  71.             var rid = this.recurrenceId;
  72.             var cal = this.calendar;
  73.             // some unused delim character:
  74.             this.mHashId = [encodeURIComponent(this.id),
  75.                             rid ? rid.getInTimezone(UTC()).icalString : "",
  76.                             cal ? encodeURIComponent(cal.id) : ""].join("#");
  77.         }
  78.         return this.mHashId;
  79.     },
  80.  
  81.     get id() {
  82.         return this.getProperty("UID");
  83.     },
  84.     set id(uid) {
  85.         this.modify();
  86.         this.mHashId = null; // recompute hashId
  87.         return this.setProperty("UID", uid);
  88.     },
  89.  
  90.     get recurrenceId() {
  91.         return this.getProperty("RECURRENCE-ID");
  92.     },
  93.     set recurrenceId(rid) {
  94.         this.modify();
  95.         this.mHashId = null; // recompute hashId
  96.         return this.setProperty("RECURRENCE-ID", rid);
  97.     },
  98.  
  99.     mParentItem: null,
  100.     get parentItem() {
  101.         if (this.mParentItem)
  102.             return this.mParentItem;
  103.         else
  104.             return this;
  105.     },
  106.     set parentItem(value) {
  107.         if (this.mImmutable)
  108.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  109.         this.mIsProxy = true;
  110.         this.mParentItem = value;
  111.     },
  112.  
  113.     initializeProxy: function (aParentItem) {
  114.         if (this.mImmutable)
  115.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  116.  
  117.         if (this.mParentItem != null)
  118.             throw Components.results.NS_ERROR_FAILURE;
  119.  
  120.         this.mParentItem = aParentItem;
  121.         this.mCalendar = aParentItem.mCalendar;
  122.         this.mIsProxy = true;
  123.     },
  124.  
  125.     //
  126.     // calIItemBase
  127.     //
  128.     mImmutable: false,
  129.     get isMutable() { return !this.mImmutable; },
  130.  
  131.     mDirty: false,
  132.     modify: function() {
  133.         if (this.mImmutable)
  134.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  135.         this.mDirty = true;
  136.     },
  137.  
  138.     ensureNotDirty: function() {
  139.         if (!this.mDirty)
  140.             return;
  141.  
  142.         if (this.mImmutable) {
  143.             dump ("### Something tried to undirty a dirty immutable event!\n");
  144.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  145.         }
  146.  
  147.         this.setProperty("LAST-MODIFIED", jsDateToDateTime(new Date()));
  148.         this.mDirty = false;
  149.     },
  150.  
  151.     makeItemBaseImmutable: function() {
  152.         if (this.mImmutable)
  153.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  154.  
  155.         // make all our components immutable
  156.         if (this.mRecurrenceInfo)
  157.             this.mRecurrenceInfo.makeImmutable();
  158.  
  159.         if (this.mOrganizer)
  160.             this.mOrganizer.makeImmutable();
  161.         if (this.mAttendees) {
  162.             for (var i = 0; i < this.mAttendees.length; i++)
  163.                 this.mAttendees[i].makeImmutable();
  164.         }
  165.  
  166.         var e = this.mProperties.enumerator;
  167.         while (e.hasMoreElements()) {
  168.             var prop = e.getNext();
  169.             var val = prop.value;
  170.  
  171.             if (prop.value instanceof Components.interfaces.calIDateTime) {
  172.                 if (prop.value.isMutable)
  173.                     prop.value.makeImmutable();
  174.             }
  175.         }
  176.  
  177.         if (this.alarmOffset) {
  178.             this.alarmOffset.makeImmutable();
  179.             if (this.alarmLastAck) {
  180.                 this.alarmLastAck.makeImmutable();
  181.             }
  182.         }
  183.  
  184.         this.ensureNotDirty();
  185.         this.mImmutable = true;
  186.     },
  187.  
  188.     hasSameIds: function(that) {
  189.         return (that && this.id == that.id &&
  190.                 (this.recurrenceId == that.recurrenceId || // both null
  191.                  (this.recurrenceId && that.recurrenceId &&
  192.                   this.recurrenceId.compare(that.recurrenceId) == 0)));
  193.     },
  194.  
  195.     // initialize this class's members
  196.     initItemBase: function () {
  197.         var now = jsDateToDateTime(new Date());
  198.  
  199.         this.mProperties = new calPropertyBag();
  200.         this.mPropertyParams = {};
  201.  
  202.         this.setProperty("CREATED", now.clone());
  203.         this.setProperty("LAST-MODIFIED", now.clone());
  204.         this.setProperty("DTSTAMP", now);
  205.  
  206.         this.mAttendees = null;
  207.  
  208.         this.mRecurrenceInfo = null;
  209.  
  210.         this.mAttachments = null;
  211.     },
  212.  
  213.     // for subclasses to use; copies the ItemBase's values
  214.     // into m. aNewParent is optional
  215.     cloneItemBaseInto: function (m, aNewParent) {
  216.         this.ensureNotDirty();
  217.  
  218.         m.mImmutable = false;
  219.         m.mIsProxy = this.mIsProxy;
  220.         m.mParentItem = aNewParent || this.mParentItem;
  221.         m.mHashId = this.mHashId;
  222.         m.mCalendar = this.mCalendar;
  223.         if (this.mRecurrenceInfo) {
  224.             m.mRecurrenceInfo = this.mRecurrenceInfo.clone();
  225.             m.mRecurrenceInfo.item = m;
  226.         }
  227.  
  228.         if (this.mOrganizer) {
  229.             m.mOrganizer = this.mOrganizer.clone();
  230.         }
  231.  
  232.         if (this.mAttendees) {
  233.             m.mAttendees = new Array(this.mAttendees.length);
  234.             for (var i = 0; i < this.mAttendees.length; i++)
  235.                 m.mAttendees[i] = this.mAttendees[i].clone();
  236.         }
  237.         else
  238.             m.mAttendees = null;
  239.  
  240.         m.mProperties = new calPropertyBag();
  241.         var e = this.mProperties.enumerator;
  242.         while (e.hasMoreElements()) {
  243.             var prop = e.getNext();
  244.             var name = prop.name;
  245.             var val = prop.value;
  246.  
  247.             if (val instanceof Components.interfaces.calIDateTime) {
  248.                 val = val.clone();
  249.             }
  250.  
  251.             m.mProperties.setProperty(name, val);
  252.  
  253.             var propBucket = this.mPropertyParams[name];
  254.             if (propBucket) {
  255.                 var newBucket = {};
  256.                 for (var param in propBucket) {
  257.                     newBucket[param] = propBucket[param];
  258.                 }
  259.                 m.mPropertyParams[name] = newBucket;
  260.             }
  261.         }
  262.  
  263.         m.mDirty = false;
  264.  
  265.         // these need fixing
  266.         m.mAttachments = this.mAttachments;
  267.  
  268.         // Clone any alarm info that exists, set it to null if it doesn't
  269.         if (this.alarmOffset) {
  270.             m.alarmOffset = this.alarmOffset.clone();
  271.             if (this.alarmLastAck) {
  272.                 m.alarmLastAck = this.alarmLastAck.clone();
  273.             } else {
  274.                 m.alarmLastAck = null;
  275.             }
  276.         } else {
  277.             m.alarmOffset = null;
  278.         }
  279.         m.alarmRelated = this.alarmRelated;
  280.  
  281.         return m;
  282.     },
  283.  
  284.     get lastModifiedTime() {
  285.         this.ensureNotDirty();
  286.         return this.getProperty("LAST-MODIFIED");
  287.     },
  288.  
  289.     get stampTime() {
  290.         var prop = this.getProperty("DTSTAMP");
  291.         if (prop && prop.isValid)
  292.             return prop;
  293.         return this.getProperty("LAST-MODIFIED");
  294.     },
  295.  
  296.     updateStampTime: function() {
  297.         // can't update the stamp time on an immutable event
  298.         if (this.mImmutable)
  299.             return;
  300.  
  301.         this.modify();
  302.         this.setProperty("DTSTAMP", jsDateToDateTime(new Date()));
  303.     },
  304.  
  305.     get unproxiedPropertyEnumerator() {
  306.         return this.mProperties.enumerator;
  307.     },
  308.  
  309.     get propertyEnumerator() {
  310.         if (this.mIsProxy) {
  311.             // nsISimpleEnumerator sucks.  It really, really sucks.
  312.             // The interface is badly defined, it's not clear
  313.             // what happens if you just keep calling getNext() without
  314.             // calling hasMoreElements in between, which seems like more
  315.             // of an informational thing.  An interface with
  316.             // "advance()" which returns true or false, and with "item()",
  317.             // which returns the item the enumerator is pointing to, makes
  318.             // far more sense.  Right now we have getNext() doing both
  319.             // item returning and enumerator advancing, which makes
  320.             // no sense.
  321.             return {
  322.                 firstEnumerator: this.mProperties.enumerator,
  323.                 secondEnumerator: this.mParentItem.propertyEnumerator,
  324.                 handledProperties: { },
  325.  
  326.                 currentItem: null,
  327.  
  328.                 QueryInterface: function(aIID) {
  329.                     if (!aIID.equals(Components.interfaces.nsISimpleEnumerator) ||
  330.                         !aIID.equals(Components.interfaces.nsISupports))
  331.                     {
  332.                         throw Components.results.NS_ERROR_NO_INTERFACE;
  333.                     }
  334.                     return this;
  335.                 },
  336.  
  337.                 hasMoreElements: function() {
  338.                     if (!this.secondEnumerator)
  339.                         return false;
  340.  
  341.                     if (this.firstEnumerator) {
  342.                         var moreFirst = this.firstEnumerator.hasMoreElements();
  343.                         if (moreFirst) {
  344.                             this.currentItem = this.firstEnumerator.getNext();
  345.                             this.handledProperties[this.currentItem.name] = true;
  346.                             return true;
  347.                         }
  348.                         this.firstEnumerator = null;
  349.                     }
  350.  
  351.                     var moreSecond = this.secondEnumerator.hasMoreElements();
  352.                     if (moreSecond) {
  353.                         while (this.currentItem.name in this.handledProperties &&
  354.                                this.secondEnumerator.hasMoreElements())
  355.                         do {
  356.                             this.currentItem = this.secondEnumerator.getNext();
  357.                         } while (this.currentItem.name in this.handledProperties &&
  358.                                  ((this.currentItem = null) == null) && // hack
  359.                                  this.secondEnumerator.hasMoreElements());
  360.  
  361.                         if (!this.currentItem)
  362.                             return false;
  363.  
  364.                         return true;
  365.                     }
  366.  
  367.                     this.secondEnumerator = null;
  368.  
  369.                     return false;
  370.                 },
  371.  
  372.                 getNext: function() {
  373.                     if (!this.currentItem)
  374.                         throw Components.results.NS_ERROR_UNEXPECTED;
  375.  
  376.                     var rval = this.currentItem;
  377.                     this.currentItem = null;
  378.                     return rval;
  379.                 }
  380.             };
  381.         } else {
  382.             return this.mProperties.enumerator;
  383.         }
  384.     },
  385.  
  386.     // The has/get/getUnproxied/set/deleteProperty methods are case-insensitive.
  387.     getProperty: function (aName) {
  388.         aName = aName.toUpperCase();
  389.         var aValue = this.mProperties.getProperty(aName);
  390.         if ((aValue === null) && this.mIsProxy) {
  391.             aValue = this.mParentItem.getProperty(aName);
  392.         }
  393.         return aValue;
  394.     },
  395.  
  396.     getUnproxiedProperty: function (aName) {
  397.         return this.mProperties.getProperty(aName.toUpperCase());
  398.     },
  399.  
  400.     hasProperty: function (aName) {
  401.         return (this.getProperty(aName.toUpperCase()) != null);
  402.     },
  403.  
  404.     setProperty: function (aName, aValue) {
  405.         if (aName == "LAST-MODIFIED") {
  406.             this.mDirty = false;
  407.         } else {
  408.             this.modify();
  409.         }
  410.         this.mProperties.setProperty(aName.toUpperCase(), aValue);
  411.     },
  412.  
  413.     deleteProperty: function (aName) {
  414.         this.modify();
  415.         this.mProperties.deleteProperty(aName.toUpperCase());
  416.     },
  417.  
  418.     getPropertyParameter: function getPP(aPropName, aParamName) {
  419.         return this.mPropertyParams[aPropName][aParamName];
  420.     },
  421.  
  422.     getAttendees: function (countObj) {
  423.         if (!this.mAttendees && this.mIsProxy && this.mParentItem) {
  424.             this.mAttendees = this.mParentItem.getAttendees(countObj);
  425.         }
  426.         if (this.mAttendees) {
  427.             countObj.value = this.mAttendees.length;
  428.             return this.mAttendees.concat([]); // clone
  429.         }
  430.         else {
  431.             countObj.value = 0;
  432.             return [];
  433.         }
  434.     },
  435.  
  436.     getAttendeeById: function (id) {
  437.         var attendees = this.getAttendees({});
  438.         var lowerCaseId = id.toLowerCase();
  439.         for each (var attendee in attendees) {
  440.             // This match must be case insensitive to deal with differing
  441.             // cases of things like MAILTO:
  442.             if (attendee.id.toLowerCase() == lowerCaseId) {
  443.                 return attendee;
  444.             }
  445.         }
  446.         return null;
  447.     },
  448.  
  449.     removeAttendee: function (attendee) {
  450.         this.modify();
  451.         var found = false, newAttendees = [];
  452.         var attendees = this.getAttendees({});
  453.         var attIdLowerCase =attendee.id.toLowerCase();
  454.  
  455.         for (var i = 0; i < attendees.length; i++) {
  456.             if (attendees[i].id.toLowerCase() != attIdLowerCase)
  457.                 newAttendees.push(attendees[i]);
  458.             else
  459.                 found = true;
  460.         }
  461.         if (found)
  462.             this.mAttendees = newAttendees;
  463.         else
  464.             throw Component.results.NS_ERROR_INVALID_ARG;
  465.     },
  466.  
  467.     removeAllAttendees: function() {
  468.         this.modify();
  469.         this.mAttendees = [];
  470.     },
  471.  
  472.     addAttendee: function (attendee) {
  473.         this.modify();
  474.         this.mAttendees = this.getAttendees({});
  475.         this.mAttendees.push(attendee);
  476.     },
  477.  
  478.     mCalendar: null,
  479.     get calendar () {
  480.         return this.mCalendar;
  481.     },
  482.  
  483.     set calendar (v) {
  484.         if (this.mImmutable)
  485.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  486.         this.mHashId = null; // recompute hashId
  487.         this.mCalendar = v;
  488.     },
  489.  
  490.     mOrganizer: null,
  491.     get organizer() {
  492.         if (!this.mOrganizer && this.mIsProxy && this.mParentItem) {
  493.             return this.mParentItem.organizer;
  494.         }
  495.         else
  496.             return this.mOrganizer;
  497.     },
  498.  
  499.     set organizer(v) {
  500.         this.modify();
  501.         this.mOrganizer = v;
  502.     },
  503.  
  504.     /* MEMBER_ATTR(mIcalString, "", icalString), */
  505.     get icalString() {
  506.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  507.     },
  508.  
  509.     set icalString() {
  510.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  511.     },
  512.  
  513.     // All of these property names must be in upper case for isPropertyPromoted to
  514.     // function correctly. The has/get/getUnproxied/set/deleteProperty interfaces
  515.     // are case-insensitive, but these are not.
  516.     itemBasePromotedProps: {
  517.         "CREATED": true,
  518.         "UID": true,
  519.         "LAST-MODIFIED": true,
  520.         "SUMMARY": true,
  521.         "PRIORITY": true,
  522.         "METHOD": true,
  523.         "STATUS": true,
  524.         "CLASS": true,
  525.         "DTSTAMP": true,
  526.         "RRULE": true,
  527.         "EXDATE": true,
  528.         "RDATE": true,
  529.         "ATTENDEE": true,
  530.         "ORGANIZER": true,
  531.         "RECURRENCE-ID": true
  532.     },
  533.  
  534.     icsBasePropMap: [
  535.     { cal: "CREATED", ics: "createdTime" },
  536.     { cal: "LAST-MODIFIED", ics: "lastModified" },
  537.     { cal: "DTSTAMP", ics: "stampTime" },
  538.     { cal: "UID", ics: "uid" },
  539.     { cal: "SUMMARY", ics: "summary" },
  540.     { cal: "PRIORITY", ics: "priority" },
  541.     { cal: "STATUS", ics: "status" },
  542.     { cal: "CLASS", ics: "icalClass" },
  543.     { cal: "RECURRENCE-ID", ics: "recurrenceId" } ],
  544.  
  545.     mapPropsFromICS: function(icalcomp, propmap) {
  546.         for (var i = 0; i < propmap.length; i++) {
  547.             var prop = propmap[i];
  548.             var val = icalcomp[prop.ics];
  549.             if (val != null && val != ICAL.INVALID_VALUE)
  550.                 this.setProperty(prop.cal, val);
  551.         }
  552.     },
  553.  
  554.     mapPropsToICS: function(icalcomp, propmap) {
  555.         for (var i = 0; i < propmap.length; i++) {
  556.             var prop = propmap[i];
  557.             var val = this.getProperty(prop.cal);
  558.             if (val != null && val != ICAL.INVALID_VALUE)
  559.                 icalcomp[prop.ics] = val;
  560.         }
  561.     },
  562.  
  563.     setItemBaseFromICS: function (icalcomp) {
  564.         this.modify();
  565.  
  566.         this.mapPropsFromICS(icalcomp, this.icsBasePropMap);
  567.  
  568.         for (var attprop = icalcomp.getFirstProperty("ATTENDEE");
  569.              attprop;
  570.              attprop = icalcomp.getNextProperty("ATTENDEE")) {
  571.             
  572.             var att = new CalAttendee();
  573.             att.icalProperty = attprop;
  574.             this.addAttendee(att);
  575.         }
  576.  
  577.         var orgprop = icalcomp.getFirstProperty("ORGANIZER");
  578.         if (orgprop) {
  579.             var org = new CalAttendee();
  580.             org.icalProperty = orgprop;
  581.             org.isOrganizer = true;
  582.             this.mOrganizer = org;
  583.         }
  584.  
  585.         // find recurrence properties
  586.         var rec = null;
  587.         for (var recprop = icalcomp.getFirstProperty("ANY");
  588.              recprop;
  589.              recprop = icalcomp.getNextProperty("ANY"))
  590.         {
  591.             var ritem = null;
  592.             if (recprop.propertyName == "RRULE" ||
  593.                 recprop.propertyName == "EXRULE")
  594.             {
  595.                 ritem = new CalRecurrenceRule();
  596.             } else if (recprop.propertyName == "RDATE" ||
  597.                        recprop.propertyName == "EXDATE")
  598.             {
  599.                 ritem = new CalRecurrenceDate();
  600.             } else {
  601.                 continue;
  602.             }
  603.  
  604.             ritem.icalProperty = recprop;
  605.  
  606.             if (!rec) {
  607.                 rec = new CalRecurrenceInfo();
  608.                 rec.item = this;
  609.             }
  610.  
  611.             rec.appendRecurrenceItem(ritem);
  612.         }
  613.         this.mRecurrenceInfo = rec;
  614.  
  615.         var alarmComp = icalcomp.getFirstSubcomponent("VALARM");
  616.         if (alarmComp) {
  617.             var triggerProp = alarmComp.getFirstProperty("TRIGGER");
  618.             // Really, really old Sunbird/Calendar versions didn't give us a
  619.             // trigger.
  620.             if (!triggerProp) {
  621.                 Components.utils.reportError("No trigger property for alarm on item: "+this.id);
  622.                 // No parsing happens after alarms, so just return
  623.                 return;
  624.             }
  625.             var duration = Components.classes["@mozilla.org/calendar/duration;1"]
  626.                                      .createInstance(Components.interfaces.calIDuration);
  627.             duration.icalString = triggerProp.valueAsIcalString;
  628.             this.alarmOffset = duration;
  629.  
  630.             var related = triggerProp.getParameter("RELATED");
  631.             if (related && related == "END")
  632.                 this.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_END;
  633.             else
  634.                 this.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_START;
  635.  
  636.             var lastAck = alarmComp.getFirstProperty("X-MOZ-LASTACK");
  637.             if (lastAck) {
  638.                 var lastAckTime = Components.classes["@mozilla.org/calendar/datetime;1"]
  639.                                             .createInstance(Components.interfaces.calIDateTime);
  640.                 lastAckTime.icalString = lastAck.valueAsIcalString;
  641.                 this.alarmLastAck = lastAckTime;
  642.             }
  643.  
  644.             var email = alarmComp.getFirstProperty("X-EMAILADDRESS");
  645.             if (email)
  646.                 this.setProperty("alarmEmailAddress", email.value);
  647.         }
  648.     },
  649.  
  650.     importUnpromotedProperties: function (icalcomp, promoted) {
  651.         for (var prop = icalcomp.getFirstProperty("ANY");
  652.              prop;
  653.              prop = icalcomp.getNextProperty("ANY")) {
  654.             if (!promoted[prop.propertyName]) {
  655.                 this.setProperty(prop.propertyName, prop.value);
  656.                 var param = prop.getFirstParameterName();
  657.                 while (param) {
  658.                     if (!(prop.propertyName in this.mPropertyParams)) {
  659.                         this.mPropertyParams[prop.propertyName] = {};
  660.                     }
  661.                     this.mPropertyParams[prop.propertyName][param] = prop.getParameter(param);
  662.                     param = prop.getNextParameterName();
  663.                 }
  664.             }
  665.         }
  666.     },
  667.  
  668.     // This method is case-insensitive.
  669.     isPropertyPromoted: function (name) {
  670.         return (this.itemBasePromotedProps[name.toUpperCase()]);
  671.     },
  672.  
  673.     get icalComponent() {
  674.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  675.     },
  676.  
  677.     get generation() {
  678.         if (this.mGeneration === undefined) {
  679.             var gen = this.getProperty("X-MOZ-GENERATION");
  680.             this.mGeneration = (gen ? parseInt(gen) : 0);
  681.         }
  682.         return this.mGeneration;
  683.     },
  684.     set generation(aValue) {
  685.         this.modify();
  686.         this.mGeneration = aValue;
  687.         this.setProperty("X-MOZ-GENERATION", String(aValue));
  688.         return aValue;
  689.     },
  690.  
  691.     fillIcalComponentFromBase: function (icalcomp) {
  692.         // Make sure that the LMT and ST are updated
  693.         this.updateStampTime();
  694.         this.ensureNotDirty();
  695.  
  696.         this.mapPropsToICS(icalcomp, this.icsBasePropMap);
  697.  
  698.         if (this.mOrganizer)
  699.             icalcomp.addProperty(this.mOrganizer.icalProperty);
  700.         var attendees = this.getAttendees({});
  701.         if (attendees.length > 0) {
  702.           for (var i = 0; i < attendees.length; i++) {
  703.             icalcomp.addProperty(attendees[i].icalProperty);
  704.           }
  705.         }
  706.  
  707.         if (this.mRecurrenceInfo) {
  708.             var ritems = this.mRecurrenceInfo.getRecurrenceItems({});
  709.             for (i in ritems) {
  710.                 icalcomp.addProperty(ritems[i].icalProperty);
  711.             }
  712.         }
  713.         
  714.         if (this.alarmOffset) {
  715.             var icssvc = getIcsService();
  716.             var alarmComp = icssvc.createIcalComponent("VALARM");
  717.  
  718.             var triggerProp = icssvc.createIcalProperty("TRIGGER");
  719.             triggerProp.valueAsIcalString = this.alarmOffset.icalString;
  720.  
  721.             if (this.alarmRelated == Components.interfaces.calIItemBase.ALARM_RELATED_END)
  722.                 triggerProp.setParameter("RELATED", "END");
  723.  
  724.             alarmComp.addProperty(triggerProp);
  725.  
  726.             if (this.alarmLastAck) {
  727.                 var lastAck = icssvc.createIcalProperty("X-MOZ-LASTACK");
  728.                 lastAck.valueAsIcalString = this.alarmLastAck.icalString;
  729.                 alarmComp.addProperty(lastAck);
  730.             }
  731.  
  732.             // We don't use this, but the ics-spec requires it
  733.             var descProp = icssvc.createIcalProperty("DESCRIPTION");
  734.             descProp.value = "Mozilla Alarm: "+ this.title;
  735.             alarmComp.addProperty(descProp);
  736.  
  737.             var actionProp = icssvc.createIcalProperty("ACTION");
  738.             actionProp.value = "DISPLAY";
  739.  
  740.             if (this.getProperty("alarmEmailAddress")) {
  741.                 var emailProp = icssvc.createIcalProperty("X-EMAILADDRESS");
  742.                 emailProp.value = this.getProperty("alarmEmailAddress");
  743.                 actionProp.value = "EMAIL";
  744.                 alarmComp.addProperty(emailProp);
  745.             }
  746.  
  747.             alarmComp.addProperty(actionProp);
  748.  
  749.             icalcomp.addSubcomponent(alarmComp);
  750.         }
  751.     },
  752.     
  753.     getOccurrencesBetween: function(aStartDate, aEndDate, aCount) {
  754.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  755.     }
  756. };
  757.  
  758. makeMemberAttr(calItemBase, "CREATED", null, "creationDate", true);
  759. makeMemberAttr(calItemBase, "SUMMARY", null, "title", true);
  760. makeMemberAttr(calItemBase, "PRIORITY", 0, "priority", true);
  761. makeMemberAttr(calItemBase, "CLASS", "PUBLIC", "privacy", true);
  762. makeMemberAttr(calItemBase, "STATUS", null, "status", true);
  763. makeMemberAttr(calItemBase, "ALARMTIME", null, "alarmTime", true);
  764.  
  765. makeMemberAttr(calItemBase, "mRecurrenceInfo", null, "recurrenceInfo");
  766. makeMemberAttr(calItemBase, "mAttachments", null, "attachments");
  767. makeMemberAttr(calItemBase, "mProperties", null, "properties");
  768.  
  769. function makeMemberAttr(ctor, varname, dflt, attr, asProperty)
  770. {
  771.     // XXX handle defaults!
  772.     var getter = function () {
  773.         if (asProperty)
  774.             return this.getProperty(varname);
  775.         else
  776.             return this[varname];
  777.     };
  778.     var setter = function (v) {
  779.         this.modify();
  780.         if (asProperty)
  781.             return this.setProperty(varname, v);
  782.         else
  783.             return (this[varname] = v);
  784.     };
  785.     ctor.prototype.__defineGetter__(attr, getter);
  786.     ctor.prototype.__defineSetter__(attr, setter);
  787. }
  788.  
  789. //
  790. // helper functions
  791. //
  792.  
  793. function icalFromString(str)
  794. {
  795.     return getIcsService().parseICS(str, null);
  796. }
  797.  
  798. function icalProp(kind)
  799. {
  800.     return getIcsService().createIcalProperty(kind);
  801. }
  802.